package xeroxware.framework.xplatform;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import com.tobesoft.xplatform.data.DataSet;
import com.tobesoft.xplatform.data.DataSetList;
import com.tobesoft.xplatform.data.DataTypes;
import com.tobesoft.xplatform.data.PlatformData;
import com.tobesoft.xplatform.data.Variable;
import com.tobesoft.xplatform.data.VariableList;
//import oracle.sql.CLOB;

import xeroxware.framework.util.CommonUtil;

/**
 * <pre>
 * XplatformDTO (Xplatform Data Transfer Object)
 * - Xplatform과 Transaction 처리하는 Data 객체
 * - VariableList/DatasetList 객체로 구성
 * </pre>
 * 
 * @since 2013. 1. 9.
 * @author XEROXER
 */
public class XplatformDTO implements Serializable {

	private static final long serialVersionUID = 3627241502883773295L;

	private VariableList variableList;

	private DataSetList datasetList;

	/**
	 * 신규 XplatformDTO 객체를 생성한다.
	 */
	public XplatformDTO() {
		variableList = new VariableList();
		datasetList = new DataSetList();
	}

	/**
	 * PlatformData 정보로 XplatformDTO 객체를 생성한다.
	 * 
	 * @param platformData
	 */
	public XplatformDTO(PlatformData platformData) {
		variableList = platformData.getVariableList();
		datasetList = platformData.getDataSetList();
	}

	/**
	 * XplatformDTO 객체의 정보로 PlatformData 객체를 생성하여 반환한다.
	 * 
	 * @return PlatformData
	 */
	public PlatformData getPlatformData() {

		PlatformData platformData = new PlatformData();

		platformData.setVariableList(variableList);
		platformData.setDataSetList(datasetList);

		return platformData;
	}

	/**
	 * VariableList 객체를 반환한다.
	 * 
	 * @return VariableList
	 */
	public VariableList getVariableList() {
		return variableList;
	}

	/**
	 * DataSetList 객체를 반환한다.
	 * 
	 * @return DataSetList
	 */
	public DataSetList getDataSetLst() {
		return datasetList;
	}

	/**
	 * VariableList 정보를 설정한다.
	 * 
	 * @param varList
	 */
	public void setVariableList(VariableList varList) {
		variableList = varList;
	}

	/**
	 * Map 정보로 VariableList 정보를 설정한다.
	 * 
	 * @param map
	 */
	public void setVariableList(Map<String, Object> map) {

		variableList = new VariableList();
		Iterator<String> it = map.keySet().iterator();
		String key = null;

		while (it.hasNext()) {
			key = it.next();

			variableList.add(key, map.get(key));
		}
	}

	/**
	 * DataSetList 정보를 설정한다.
	 * 
	 * @param dsList
	 */
	public void setDataSetList(DataSetList dsList) {
		datasetList = dsList;
	}

	/**
	 * Map 정보로 DataSetList 정보를 설정한다.
	 * 
	 * @param map
	 */
	public void setDataSetList(Map<String, List<Map<String, Object>>> map) throws Exception {

		datasetList = new DataSetList();
		Iterator<String> it = map.keySet().iterator();
		String key = null;

		while (it.hasNext()) {
			key = it.next();

			datasetList.add(convertDataset(key, map.get(key)));
		}
	}

	/**
	 * 식별자(key)에 해당하는 Variable의 값을 반환한다.
	 * 
	 * @param key
	 * @return Variable
	 */
	public String getVariable(String key) {
		return variableList.getString(key);
	}

	/**
	 * 위치(index)에 해당하는 Variable의 값을 반환한다.
	 * 
	 * @param index
	 * @return Variable
	 */
	public String getVariable(int index) {
		return variableList.getString(index);
	}

	/**
	 * 식별자(key)에 해당하는 DataSet을 반환한다.
	 * 
	 * @param key
	 * @return DataSet
	 */
	public DataSet getDataSet(String key) {
		return datasetList.get(key);
	}

	/**
	 * 위치(index)에 해당하는 DataSet을 반환한다.
	 * 
	 * @param index
	 * @return DataSet
	 */
	public DataSet getDataSet(int index) {
		return datasetList.get(index);
	}

	/**
	 * 값(value)을 해당하는 식별자(key)의 Variable에 설정한다.
	 * 
	 * @param key
	 * @param value
	 */
	public void setVariable(String key, Object value) {
		variableList.set(key, value);
	}

	/**
	 * 값(value)을 해당하는 위치(index)의 Variable에 설정한다.
	 * 
	 * @param index
	 * @param value
	 */
	public void setVariable(int index, Object value) {
		variableList.set(index, value);
	}

	/**
	 * DataSet을 해당하는 위치(index)에 설정한다.
	 * 
	 * @param index
	 * @param ds
	 */
	public void setDataSet(int index, DataSet ds) {
		datasetList.set(index, ds);
	}

	/**
	 * 변환된 DataSet을 해당하는 위치(index)에 설정한다.
	 * 
	 * @param index
	 * @param id
	 * @param list
	 */
	public void setDataSet(int index, String id, List<Map<String, Object>> list) throws Exception {
		datasetList.set(index, convertDataset(id, list));
	}

	/**
	 * 식별자(key)와 값(value)을 가지는 Variable을 추가한다.
	 * 
	 * @param key
	 * @param value
	 */
	public void addVariable(String key, Object value) {
		variableList.add(key, value);
	}

	/**
	 * DataSet을 추가한다.
	 * 
	 * @param ds
	 */
	public void addDataSet(DataSet ds) {
		datasetList.add(ds);
	}

	/**
	 * DataSet 형태로 변환하여 추가한다.
	 * 
	 * @param id
	 * @param list
	 */
	public void addDataSet(String id, List<Map<String, Object>> list) throws Exception {
		datasetList.add(convertDataset(id, list));
	}

	/**
	 * DataSet 형태로 변환하여 추가한다.
	 * 
	 * @param id
	 * @param map
	 */
	public void addDataSet(String id, Map<String, Object> map) {
		datasetList.add(convertDataset(id, map));
	}

	/**
	 * 해당하는 식별자(key)의 Variable을 삭제한다.
	 * 
	 * @param key
	 * @return 삭제된 Variable
	 */
	public Variable removeVariable(String key) {
		return variableList.remove(key);
	}

	/**
	 * 해당하는 위치(index)의 Variable을 삭제한다.
	 * 
	 * @param index
	 * @return 삭제된 Variable
	 */
	public Variable removeVariable(int index) {
		return variableList.remove(index);
	}

	/**
	 * 해당하는 식별자(key)의 DataSet을 삭제한다.
	 * 
	 * @param key
	 * @return 삭제된 DataSet
	 */
	public DataSet removeDataSet(String key) {
		return datasetList.remove(key);
	}

	/**
	 * 해당하는 위치(index)의 DataSet을 삭제한다.
	 * 
	 * @param index
	 * @return 삭제된 DataSet
	 */
	public DataSet removeDataSet(int index) {
		return datasetList.remove(index);
	}

	/**
	 * Variable의 갯수를 반환한다.
	 * 
	 * @return Variable의 갯수
	 */
	public int getVariableSize() {
		return variableList.size();
	}

	/**
	 * DataSet의 갯수를 반환한다.
	 * 
	 * @return DataSet의 갯수
	 */
	public int getDataSetSize() {
		return datasetList.size();
	}

	/**
	 * 데이터셋의 ConstColumn 갯수를 반환한다.
	 * 
	 * @param ds
	 * @return ConstColumn의 갯수
	 */
	public int getConstantColumnCount(DataSet ds) {

		int iCount = 0;

		for (int i = 0; i < ds.getColumnCount(); i++) {
			if (ds.getColumn(i).isConstant()) {
				iCount++;
			}
		}

		return iCount;
	}

	/**
	 * 처리결과를 VariableList에 설정한다.
	 * 
	 * @param code
	 *            결과 코드 (오류일 경우 음수)
	 * @param message
	 *            결과 메시지
	 */
	public void setResultMessage(int code, String message) {
		variableList.add("ErrorCode", code);
		variableList.add("ErrorMsg", message);
	}

	/**
	 * VariableList 정보를 Map 형태로 변환하여 반환한다.
	 * 
	 * @return Map 형식의 VariableList 정보
	 */
	@SuppressWarnings("unchecked")
	public Map<String, Object> getVariableListForMap() {

		Iterator<String> it = variableList.keyList().iterator();
		Map<String, Object> map = new HashMap<String, Object>();
		String key = null;

		while (it.hasNext()) {
			key = it.next();

			map.put(key, variableList.getString(key));
		}

		return map;
	}

	/**
	 * Constant Column 정보를 Map 형태로 반환한다.
	 * 
	 * @param id
	 */
	public Map<String, Object> getConstantColumnForMap(String id) {
		return getConstantColumnForMap(datasetList.get(id));
	}

	/**
	 * Constant Column 정보를 Map 형태로 반환한다.
	 * 
	 * @param index
	 */
	public Map<String, Object> getConstantColumnForMap(int index) {
		return getConstantColumnForMap(datasetList.get(index));
	}

	/**
	 * Constant Column 정보를 Map 형태로 반환한다.
	 * 
	 * @param ds
	 */
	public Map<String, Object> getConstantColumnForMap(DataSet ds) {

		Map<String, Object> map = new HashMap<String, Object>();

		map.putAll(getVariableListForMap());

		for (int i = 0; i < ds.getColumnCount(); i++) {
			if (ds.getColumn(i).isConstant()) {
				map.put(ds.getColumn(i).getName(), ds.getString(0, i));
			}
		}

		return map;
	}

	/**
	 * 식별자(key)에 해당하는 DataSet을 List 형식으로 변환하여 반환한다.
	 * 
	 * @param id
	 * @return List 형식의 DataSet
	 */
	public List<Map<String, Object>> getDataSetForList(String id) {
		return getDataSetForList(datasetList.get(id), false);
	}

	/**
	 * 위치(index)에 해당하는 DataSet을 List 형식으로 변환하여 반환한다.
	 * 
	 * @param index
	 * @return List 형식의 DataSet
	 */
	public List<Map<String, Object>> getDataSetForList(int index) {
		return getDataSetForList(datasetList.get(index), false);
	}

	/**
	 * 식별자(key)에 해당하는 DataSet을 List 형식으로 변환하여 반환한다.
	 * 
	 * @param id
	 * @param bDelete
	 *            삭제데이터 포함여부
	 * @return List 형식의 DataSet
	 */
	public List<Map<String, Object>> getDataSetForList(String id, boolean bDelete) {
		return getDataSetForList(datasetList.get(id), bDelete);
	}

	/**
	 * 위치(index)에 해당하는 DataSet을 List 형식으로 변환하여 반환한다.
	 * 
	 * @param index
	 * @param bDelete
	 *            삭제데이터 포함여부
	 * @return List 형식의 DataSet
	 */
	public List<Map<String, Object>> getDataSetForList(int index, boolean bDelete) {
		return getDataSetForList(datasetList.get(index), bDelete);
	}

	/**
	 * DataSet을 List 형식으로 변환하여 반환한다.
	 * 
	 * @param ds
	 * @param bDelete
	 *            삭제데이터 포함여부
	 * @return List 형식의 DataSet
	 */
	public List<Map<String, Object>> getDataSetForList(DataSet ds, boolean bDelete) {

		List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
		Map<String, Object> map = null;
		String key = null;
		String value = null;

		/*
		 * 삭제데이터를 담는다.
		 */
		if (bDelete) {
			for (int i = 0; i < ds.getRemovedRowCount(); i++) {
				map = new HashMap<String, Object>();
				map.put("_ROW_TYPE", "D");
				map.putAll(getVariableListForMap());

				for (int j = 0; j < ds.getColumnCount(); j++) {
					key = ds.getColumn(j).getName();
					value = ds.getRemovedStringData(i, j);

					if (key.lastIndexOf("_YN") >= 0) {
						if ("1".equals(value)) {
							map.put(key, "Y");
						} else if ("0".equals(value)) {
							map.put(key, "N");
						} else {
							map.put(key, value);
						}
					} else {
						map.put(key, value);
					}
				}

				list.add(map);
			}
		}

		/*
		 * 삭제 데이터를 제외한 모든 데이터를 담는다.
		 */
		for (int i = 0; i < ds.getRowCount(); i++) {
			map = new HashMap<String, Object>();

			switch (ds.getRowType(i)) {
			case DataSet.ROW_TYPE_NORMAL:
				map.put("_ROW_TYPE", "N");
				break;
			case DataSet.ROW_TYPE_INSERTED:
				map.put("_ROW_TYPE", "I");
				break;
			case DataSet.ROW_TYPE_UPDATED:
				map.put("_ROW_TYPE", "U");
				break;
			case DataSet.ROW_TYPE_DELETED:
				map.put("_ROW_TYPE", "D");
				break;
			default:
				map.put("_ROW_TYPE", "N");
				break;
			}

			map.putAll(getVariableListForMap());

			for (int j = 0; j < ds.getColumnCount(); j++) {
				key = ds.getColumn(j).getName();
				value = ds.getString(i, j);

				if (key.lastIndexOf("_YN") >= 0) {
					if ("1".equals(value)) {
						map.put(key, "Y");
					} else if ("0".equals(value)) {
						map.put(key, "N");
					} else {
						map.put(key, value);
					}
				} else {
					map.put(key, value);
				}
			}

			list.add(map);
		}

		return list;
	}

	/**
	 * DataSetList를 Map 형식으로 변환하여 반환한다.
	 * 
	 * @return Map 형식으로 변환된 DataSetList
	 */
	public Map<String, List<Map<String, Object>>> getDataSetListForMap() {
		return getDataSetListForMap(false);
	}

	/**
	 * DataSetList를 Map 형식으로 변환하여 반환한다.
	 * 
	 * @param bDelete
	 * @return Map 형식으로 변환된 DataSetList
	 */
	public Map<String, List<Map<String, Object>>> getDataSetListForMap(boolean bDelete) {

		Map<String, List<Map<String, Object>>> map = new HashMap<String, List<Map<String, Object>>>();
		DataSet ds = null;

		for (int i = 0; i < datasetList.size(); i++) {
			ds = datasetList.get(i);

			map.put(ds.getName(), getDataSetForList(ds, bDelete));
		}

		return map;
	}

	/**
	 * List 형식의 정보를 DataSet 으로 변환하여 반환한다.
	 * 
	 * @param id
	 * @param list
	 * @return DataSet
	 */
	public DataSet convertDataset(String id, List<Map<String, Object>> list) throws Exception {

		Iterator<String> it = null;
		DataSet ds = new DataSet(id);
		String key = null;
		String type = null;
		Object value = null;
		int iRow = -1;

		if (list == null || list.isEmpty()) {
			return ds;
		}

		/*
		 * Row 체크를 위한 "checked" 컬럼을 기본으로 추가한다.
		 */
		ds.addColumn("checked", DataTypes.STRING, 1);

		/*
		 * Map 객체의 KeySet 정보로 DataSet Column 메타 정보를 생성한다.
		 */
		for (Map<String, Object> map : list) {
			if (map == null || map.isEmpty()) {
				continue;
			}

			TreeMap<String, Object> treeMap = new TreeMap<String, Object>(map);
			it = treeMap.keySet().iterator();

			while (it.hasNext()) {
				key = it.next();

				if (!ds.containsColumn(key)) {
					ds.addColumn(key, DataTypes.STRING);
				}
			}
		}

		/*
		 * 메타 정보가 생성된 DataSet에 데이터를 저장한다.
		 */
		for (Map<String, Object> map : list) {
			it = map.keySet().iterator();
			iRow = ds.newRow();

			ds.set(iRow, "checked", "0");

			while (it.hasNext()) {
				key = it.next();
				value = map.get(key);

				if (CommonUtil.isNull(value)) {
					type = null;
				} else {
					type = value.getClass().getSimpleName();
				}

				if (key.lastIndexOf("_YN") >= 0) {
					if ("Y".equals(value)) {
						ds.set(iRow, key, "1");
					} else if ("N".equals(value)) {
						ds.set(iRow, key, "0");
					} else {
						ds.set(iRow, key, value);
					}
				} else {
					if ("CLOB".equals(type)) {
						//ds.set(iRow, key, clobToString((CLOB) value));
						ds.set(iRow, key, value);
					} else {
						ds.set(iRow, key, value);
					}
				}
			}
		}

		return ds;
	}

	/**
	 * Map 형식의 정보를 DataSet 으로 변환하여 반환한다.
	 * 
	 * @param id
	 * @param map
	 * @return DataSet
	 */
	public DataSet convertDataset(String id, Map<String, Object> map) {

		if (map == null || map.isEmpty()) {
			return new DataSet(id);
		}

		TreeMap<String, Object> treeMap = new TreeMap<String, Object>(map);
		Iterator<String> it = treeMap.keySet().iterator();
		DataSet ds = new DataSet(id);
		String key = null;

		/*
		 * Map 객체의 KeySet 정보로 DataSet Column 메타 정보를 생성한다.
		 */
		while (it.hasNext()) {
			key = it.next();

			ds.addConstantColumn(key, map.get(key));
		}

		return ds;
	}

	/**
	 * PlatformData 정보를 XML 정보로 반환한다.
	 * 
	 * @return PlatformData 정보
	 */
	public final String saveXml() {

		PlatformData platformData = new PlatformData();

		platformData.setVariableList(variableList);
		platformData.setDataSetList(datasetList);

		return platformData.saveXml();
	}

	/**
	 * DataSetList 정보를 Rexpert Xml 형태로 변환하여 반환한다.
	 * 
	 * @return Xml
	 */
	public final String makeRexpertXml() {

		StringBuilder result = new StringBuilder();
		String datasetXml = null;
		DataSet ds = null;

		result.append("<root>");
		result.append("\n");

		for (int i = 0; i < datasetList.size(); i++) {
			ds = datasetList.get(i);
			datasetXml = "";

			if (ds.getRowCount() <= 0 && getConstantColumnCount(ds) > 0) {
				datasetXml += "<" + ds.getName() + ">\n";
				datasetXml += "<Rows>\n";
				datasetXml += "<Row>\n";

				for (int j = 0; j < ds.getColumnCount(); j++) {
					if (ds.getColumn(j).isConstant()) {
						datasetXml += "<Col id=\"" + ds.getColumn(j).getName() + "\">";
						datasetXml += "<![CDATA[" + CommonUtil.nullToString(ds.getString(0, j))
								+ "]]>";
						datasetXml += "</Col>\n";
					}
				}

				datasetXml += "</Row>\n";
				datasetXml += "</Rows>\n";
				datasetXml += "</" + ds.getName() + ">\n";
			} else {
				datasetXml = ds.saveXml();
				datasetXml = datasetXml.replace("<Dataset id=\"" + ds.getName() + "\">",
						"<" + ds.getName() + ">");
				datasetXml = datasetXml.replace("</Dataset>", "</" + ds.getName() + ">");
			}

			result.append(datasetXml);
		}

		result.append("</root>");

		return result.toString();

	}

	/**
	 * Oracle CLOB 타입의 컬럼을 String 형태로 변환하여 반환한다.
	 * 
	 * @param clob
	 * @return
	 * @throws Exception
	 */
//	private static String clobToString(CLOB clob) throws Exception {
//
//		StringBuilder sb = new StringBuilder();
//		BufferedReader br = new BufferedReader(clob.getCharacterStream());
//		String ts = "";
//
//		while ((ts = br.readLine()) != null) {
//			sb.append(ts + "\n");
//		}
//
//		br.close();
//
//		return sb.toString();
//	}

}
